#!/bin/bash

CUR_DIR="$( cd "$( dirname "$(readlink -f "$0")" )" && pwd )"
cd $CUR_DIR

source ../../cluster_IP.conf || exit -1

updateFabricBinary="false"
createArtifactsCompose="true"
FABRIC_PATH="$GOPATH/src/github.com/hyperledger/fabric"
# FABRIC_BRANCH="C1.4"
FABRIC_BRANCH="c1.4-eth"
# FABRIC_BRANCH="main"
FABRIC_VERSION="1.4"
ORDERED_NUM=${#FABRIC_ORDERER_IPs[@]}
ORG_NUM=${#FABRIC_PEER_IPs[@]}
CHANNEL_NAME=("mychannel")
CHAINCODE_NAME="mycc"
TARGET_HOST_FABRIC_NETWORK_DIR="~/deploy-bcs/fabric-network"

function usage() {
    echo "Usage:"
    echo "    ./deploy_fabric_network.sh [up|down]"
    echo "        up: start fabric network"
    echo "        down: down fabric network"
}

function wait_sometime() {
    for((i=1; i<=$1; i++))
    do
        sleep 1s
        echo "wait ${i}s ..."
    done
}

function install_deps_for_all_nodes() {
    # install deps for all nodes
    for node in ${HOSTS_IPs[@]}
    do
        echo "==== install deps for $node ===="
        scp install-deps.sh ${HOST_USER}@${node}:~/
        ssh ${HOST_USER}@${node} "bash ~/install-deps.sh; rm ~/install-deps.sh" &
    done
    wait
    echo "Install deps for all nodes done"
}

function make_fabric_binary() {
    echo "Make fabric binary ..."
    if [ ! -d "$FABRIC_PATH" ]; then
        mkdir -p $FABRIC_PATH
        git clone https://github.com/little-czy/fabric.git $FABRIC_PATH
    fi
    cd $FABRIC_PATH
    git pull
    git checkout $FABRIC_BRANCH
    git pull
    docker rmi hyperledger/fabric-tools:$FABRIC_VERSION \
               hyperledger/fabric-peer:$FABRIC_VERSION \
               hyperledger/fabric-orderer:$FABRIC_VERSION
    make peer-docker-clean orderer-docker-clean tools-docker-clean
    make peer-docker orderer-docker tools-docker
    docker rmi $(docker images -a | grep "<none>" | awk '{print $3}')
    echo "Make fabric binary done"
}

function distribute_fabric_binarys_to_all_nodes() {
    local tag=$FABRIC_VERSION 
    cd $FABRIC_PATH
    docker save -o fabric_peer_${tag}.tar hyperledger/fabric-peer:$tag
    docker save -o fabric_orderer_${tag}.tar hyperledger/fabric-orderer:$tag
    docker save -o fabric_tools_${tag}.tar hyperledger/fabric-tools:$tag
    for node in ${HOSTS_IPs[@]}
    do
        echo "==== send fabric binarys to $node ===="
        scp fabric_peer_${tag}.tar ${HOST_USER}@${node}:~/
        scp fabric_orderer_${tag}.tar ${HOST_USER}@${node}:~/
        scp fabric_tools_${tag}.tar ${HOST_USER}@${node}:~/
        ssh ${HOST_USER}@${node} "
                docker rmi hyperledger/fabric-peer:$tag hyperledger/fabric-orderer:$tag hyperledger/fabric-tools:$tag
                docker load --input fabric_peer_${tag}.tar
                docker load --input fabric_orderer_${tag}.tar
                docker load --input fabric_tools_${tag}.tar
                docker rmi \$(docker images -a | grep '<none>' | awk '{print \$3}')

                rm fabric_peer_${tag}.tar fabric_orderer_${tag}.tar fabric_tools_${tag}.tar" &
    done
    rm fabric_peer_${tag}.tar fabric_orderer_${tag}.tar fabric_tools_${tag}.tar
    wait
    echo "Distribute fabric binarys to all nodes done"
}

function make_gen_tools() {
    cd $FABRIC_PATH
    [ ! -f ./build/bin/configtxgen ] && make configtxgen
    [ ! -f ./build/bin/cryptogen ] && make cryptogen
}

function gen_artifact_config_files() {
    cd $CUR_DIR/gen_config_yaml
    python3 gen_configtx_from_template.py -o $ORDERED_NUM -p $ORG_NUM
    python3 gen_crypto_config.py -o $ORDERED_NUM -p $ORG_NUM
}

function gen_artifact_files() {
    echo "Generate artifacts files ..."
    cd $CUR_DIR

    # system channel name defaults to "my-sys-channel"
    local SYS_CHANNEL="sys-channel"

    $FABRIC_PATH/build/bin/cryptogen generate \
        --config=./gen_config_yaml/crypto-config.yaml \
        --output="./crypto-config"

    $FABRIC_PATH/build/bin/configtxgen \
        -configPath ./gen_config_yaml/ \
        -profile OrgsOrdererGenesis \
        -channelID $SYS_CHANNEL \
        -outputBlock ./channel-artifacts/genesis.block

    for channel_name in ${CHANNEL_NAME[@]}; do
        $FABRIC_PATH/build/bin/configtxgen -profile OrgsChannel \
            -configPath ./gen_config_yaml/ \
            -outputCreateChannelTx ./channel-artifacts/${channel_name}.tx \
            -channelID $channel_name
        for ((i=1; i<=${ORG_NUM}; i++))
        do
            $FABRIC_PATH/build/bin/configtxgen -profile OrgsChannel \
                -configPath ./gen_config_yaml/ \
                -outputAnchorPeersUpdate \
                    ./channel-artifacts/Org${i}MSPanchors_${channel_name}.tx \
                -channelID $channel_name -asOrg Org${i}MSP
        done
    done
    echo "Generate artifacts files done"
}

function create_artifact_files() {
    echo "Create artifact files ..."
    make_gen_tools

    cd $CUR_DIR
    [ -d "channel-artifacts" ] && rm -r channel-artifacts
    [ -d "crypto-config" ] && rm -r crypto-config
    mkdir channel-artifacts crypto-config

    gen_artifact_config_files

    gen_artifact_files
    echo "Create artifact files done"
}

function create_compose_files() {
    echo "Create compose files ..."
    cd $CUR_DIR
    python3 ./gen_config_yaml/gen_compose_files.py -o $ORDERED_NUM -p $ORG_NUM
    echo "Create compose files done"
}

function distribute_startup_files_to_all_nodes() {
    echo "Distribute startup files to all nodes ..."
    cd $CUR_DIR
    tar -zcvf startups.tar.gz channel-artifacts crypto-config gen_config_yaml chaincode > /dev/null
    for node in ${HOSTS_IPs[@]}; do
        echo "==== send startup files to $node ===="
        scp startups.tar.gz ${HOST_USER}@${node}:~/
        ssh ${HOST_USER}@${node} "
            [ -d '${TARGET_HOST_FABRIC_NETWORK_DIR}' ] && sudo rm -rf ${TARGET_HOST_FABRIC_NETWORK_DIR}/* || \
                mkdir -p ${TARGET_HOST_FABRIC_NETWORK_DIR}
            tar -zxvf startups.tar.gz -C ${TARGET_HOST_FABRIC_NETWORK_DIR} > /dev/null
            rm startups.tar.gz" &
    done
    rm startups.tar.gz
    wait

    echo "Distribute startup files to all nodes done"
}

function down_fabric_network() {
    echo "Down fabric network ..."
    cd $CUR_DIR

    # down peer
    for ((i=0; i<${ORG_NUM}; i++)); do
        echo "==== down peer0.org${i} ===="
        ssh ${HOST_USER}@${FABRIC_PEER_IPs[$i]} "
            cd ${TARGET_HOST_FABRIC_NETWORK_DIR}/gen_config_yaml && export IMAGE_TAG='$FABRIC_VERSION'&& \
            docker-compose -f docker-compose-org$((i+1)).yaml down --volumes --remove-orphans

            # remove chaincode docker containers
            docker ps -a | grep dev-peer | awk '{print \$1}' | xargs docker rm -f > /dev/null 2>&1
            docker rmi \$(docker images -a | grep dev-peer0 | awk '{print \$3}')
            docker rmi \$(docker images -a | grep none | awk '{print \$3}')
        " &
    done

    # down orderer
    for ((i=0; i<${ORDERED_NUM}; i++)); do
        echo "==== down orderer${i} ===="
        ssh ${HOST_USER}@${FABRIC_ORDERER_IPs[$i]} "
            cd ${TARGET_HOST_FABRIC_NETWORK_DIR}/gen_config_yaml && export IMAGE_TAG='$FABRIC_VERSION'&& \
            docker-compose -f docker-compose-order$((i+1)).yaml down --volumes --remove-orphans
        " &
    done

    # down fabric-sdk
    for node in ${FABRIC_SDK_IPs[@]}; do
        echo "==== down fabric-sdk on $node ===="
        ssh ${HOST_USER}@${node} "
            sudo kill -9 \$(ps -ef | grep fabric-sdk | grep -v grep | awk '{print \$2}')
        " &
    done

    wait
    echo "Down fabric network done"
}

function start_fabric_network() {
    echo "Start fabric network ..."
    cd $CUR_DIR

    # start orderer
    for ((i=0; i<${ORDERED_NUM}; i++)); do
        echo "==== start orderer${i} ===="
        ssh ${HOST_USER}@${FABRIC_ORDERER_IPs[$i]} "
            cd ${TARGET_HOST_FABRIC_NETWORK_DIR}/gen_config_yaml && export IMAGE_TAG='$FABRIC_VERSION'&& \
            docker-compose -f docker-compose-order$((i+1)).yaml up -d
        " &
    done

    # start peer
    for ((i=0; i<${ORG_NUM}; i++)); do
        echo "==== start peer0.org${i} ===="
        ssh ${HOST_USER}@${FABRIC_PEER_IPs[$i]} "
            cd ${TARGET_HOST_FABRIC_NETWORK_DIR}/gen_config_yaml && export IMAGE_TAG='$FABRIC_VERSION'&& \
            docker-compose -f docker-compose-org$((i+1)).yaml up -d
        " &
    done

    wait
    echo "Start fabric network done"
}

function create_join_channel() {
    echo "Create & join channel ..."
    cd $CUR_DIR

    # generate channel
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== generate channel ${CHANNEL_NAME[$i]} ===="
        local port=$((i+7050))
        ssh ${HOST_USER}@${FABRIC_PEER_IPs[0]} "
            cd ${TARGET_HOST_FABRIC_NETWORK_DIR}
            docker exec cli peer channel create -o orderer$((i+1)).demo.com:${port} -c ${CHANNEL_NAME[$i]} -f ./channel-artifacts/${CHANNEL_NAME[$i]}.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/demo.com/orderers/orderer$((i+1)).demo.com/msp/tlscacerts/tlsca.demo.com-cert.pem
            docker cp cli:/opt/gopath/src/github.com/hyperledger/fabric/peer/${CHANNEL_NAME[$i]}.block ./channel-artifacts/${CHANNEL_NAME[$i]}.block
        "
        scp ${HOST_USER}@${FABRIC_PEER_IPs[0]}:${TARGET_HOST_FABRIC_NETWORK_DIR}/channel-artifacts/${CHANNEL_NAME[$i]}.block ./channel-artifacts/
    done

    # copy channel block to all peers
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== copy channel ${CHANNEL_NAME[$i]} to all peers ===="
        for ((j=0; j<${ORG_NUM}; j++)); do
            scp ./channel-artifacts/${CHANNEL_NAME[$i]}.block ${HOST_USER}@${FABRIC_PEER_IPs[$j]}:${TARGET_HOST_FABRIC_NETWORK_DIR}/channel-artifacts/${CHANNEL_NAME[$i]}.block
        done
    done

    # join channel
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== join channel ${CHANNEL_NAME[$i]} ===="
        local port=$((i+7050))
        for ((j=0; j<${ORG_NUM}; j++)); do
            ssh ${HOST_USER}@${FABRIC_PEER_IPs[$j]} "
                cd ${TARGET_HOST_FABRIC_NETWORK_DIR}
                docker cp ./channel-artifacts/${CHANNEL_NAME[$i]}.block cli:/opt/gopath/src/github.com/hyperledger/fabric/peer/${CHANNEL_NAME[$i]}.block
                docker exec cli peer channel join -b ${CHANNEL_NAME[$i]}.block
                docker exec cli peer channel update -o orderer$((i+1)).demo.com:${port} -c ${CHANNEL_NAME[$i]} -f ./channel-artifacts/Org$((j+1))MSPanchors_${CHANNEL_NAME[$i]}.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/demo.com/orderers/orderer$((i+1)).demo.com/msp/tlscacerts/tlsca.demo.com-cert.pem
            " &
        done
    done
    wait

    echo "Create & join channel done"
}

function install_chaincode() {
    echo "Install chaincode ..."
    cd $CUR_DIR

    # install chaincode
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== install chaincode on channel ${CHANNEL_NAME[$i]} ===="
        for ((j=0; j<${ORG_NUM}; j++)); do
            ssh ${HOST_USER}@${FABRIC_PEER_IPs[$j]} "
                docker exec cli peer chaincode install -n ${CHAINCODE_NAME} -v 1.0 -p github.com/chaincode/go/
            "
        done
    done

    # instantiate chaincode
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== instantiate chaincode on channel ${CHANNEL_NAME[$i]} ===="
        local port=$((i+7050))

        for ((j=1; j<=${ORG_NUM}; j++))
        do
            ORG_MSPS+=("'Org${j}MSP.member'")
        done
        ORG_MSPS_STR=$(IFS=','; echo "${ORG_MSPS[*]}")

        ssh ${HOST_USER}@${FABRIC_PEER_IPs[0]} "
            cd ${TARGET_HOST_FABRIC_NETWORK_DIR}
            docker exec cli peer chaincode instantiate -o orderer$((i+1)).demo.com:${port} -C ${CHANNEL_NAME[$i]} -n ${CHAINCODE_NAME} -v 1.0 -c '{\"Args\":[\"init\"]}' -P \"OR (${ORG_MSPS_STR})\" --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/demo.com/orderers/orderer$((i+1)).demo.com/msp/tlscacerts/tlsca.demo.com-cert.pem
            # docker exec cli peer chaincode instantiate -o orderer$((i+1)).demo.com:${port} -C ${CHANNEL_NAME[$i]} -n ${CHAINCODE_NAME} -v 1.0 -c '{\"Args\":[\"init\"]}' -P \"OutOf(3, ${ORG_MSPS_STR})\" --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/demo.com/orderers/orderer$((i+1)).demo.com/msp/tlscacerts/tlsca.demo.com-cert.pem
        "
    done

    # wait a few time for chaincode instantiate
    wait_sometime 3

    # run chaincode in other cluster
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== run chaincode on channel ${CHANNEL_NAME[$i]} ===="
        local port=$((i+7050))
        for ((j=1; j<${ORG_NUM}; j++))
        do
            ssh ${HOST_USER}@${FABRIC_PEER_IPs[$j]} "
                docker exec cli peer chaincode invoke -o orderer$((i+1)).demo.com:${port} --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/demo.com/orderers/orderer$((i+1)).demo.com/msp/tlscacerts/tlsca.demo.com-cert.pem -C ${CHANNEL_NAME[$i]} -n ${CHAINCODE_NAME} -c '{\"Args\":[\"create\",\"obj_${j}\",\"123\"]}'
            " &
        done
    done
    wait

    # test query
    for ((i=0; i<${#CHANNEL_NAME[@]}; i++)); do
        echo "==== test query on channel ${CHANNEL_NAME[$i]} ===="
        local port=$((i+7050))
        ssh ${HOST_USER}@${FABRIC_PEER_IPs[0]} "
            docker exec cli peer chaincode query -C ${CHANNEL_NAME[$i]} -n ${CHAINCODE_NAME} -c '{\"Args\":[\"read\",\"object_0\"]}'
        "
    done

    echo "Install chaincode done"
}

function fabric_network_up() {
    echo "Fabric network up ..."

    install_deps_for_all_nodes
    down_fabric_network

    # should make fabric binary
    if [ $updateFabricBinary == "true" ]; then
        make_fabric_binary
        distribute_fabric_binarys_to_all_nodes
    fi

    # should create artifacts & compose files
    if [ $createArtifactsCompose == "true" ]; then
        create_artifact_files
        create_compose_files
        distribute_startup_files_to_all_nodes
    fi

    start_fabric_network

    create_join_channel
    install_chaincode

    echo "Fabric network up done"
}

if [ $# -ne 1 ]; then
    echo "Usage: $0 [up|down]"
    exit -1
fi

if [ $1 == "up" ]; then
    fabric_network_up
elif [ $1 == "down" ]; then
    down_fabric_network
else
    usage
fi
